# coding=utf-8
# Copyright 2018 The Google AI Language Team Authors and The HuggingFace Inc. team.
# Copyright (c) 2018, NVIDIA CORPORATION.  All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
""" GLUE processors and helpers """

import logging
import os

from .file_utils import is_tf_available
from .utils import DataProcessor, InputExample, InputFeatures


if is_tf_available():
    import tensorflow as tf

logger = logging.getLogger(__name__)


def absa_convert_examples_to_features(
    examples,
    tokenizer,
    max_length=512,
    task=None,
    label_list=None,
    root_label_list=None,
    output_mode=None,
    pad_on_left=False,
    pad_token=0,
    pad_token_segment_id=0,
    mask_padding_with_zero=True,
):
    """
    Loads a data file into a list of ``InputFeatures``

    Args:
        examples: List of ``InputExamples`` or ``tf.data.Dataset`` containing the examples.
        tokenizer: Instance of a tokenizer that will tokenize the examples
        max_length: Maximum example length
        task: GLUE task
        label_list: List of labels. Can be obtained from the processor using the ``processor.get_labels()`` method
        output_mode: String indicating the output mode. Either ``regression`` or ``classification``
        pad_on_left: If set to ``True``, the examples will be padded on the left rather than on the right (default)
        pad_token: Padding token
        pad_token_segment_id: The segment ID for the padding token (It is usually 0, but can vary such as for XLNet where it is 4)
        mask_padding_with_zero: If set to ``True``, the attention mask will be filled by ``1`` for actual values
            and by ``0`` for padded values. If set to ``False``, inverts it (``1`` for padded values, ``0`` for
            actual values)

    Returns:
        If the ``examples`` input is a ``tf.data.Dataset``, will return a ``tf.data.Dataset``
        containing the task-specific features. If the input is a list of ``InputExamples``, will return
        a list of task-specific ``InputFeatures`` which can be fed to the model.

    """
    is_tf_dataset = False
    if is_tf_available() and isinstance(examples, tf.data.Dataset):
        is_tf_dataset = True
    if task is not None:
        processor = absa_processors[task]()
        if label_list is None:
            label_list = processor.get_labels()
            root_label_list = absa_processors[task]().get_root_label()
            logger.info("Using label list %s for task %s" % (label_list, task))
        if output_mode is None:
            output_mode = absa_output_modes[task]
            logger.info("Using output mode %s for task %s" % (output_mode, task))

    label_map = {label: i for i, label in enumerate(label_list)}
    root_label_map ={label:i for i,label in enumerate(root_label_list)}

    features = []
    for (ex_index, example) in enumerate(examples):
        len_examples = 0
        if is_tf_dataset:
            example = processor.get_example_from_tensor_dict(example)
            example = processor.tfds_map(example)
            len_examples = tf.data.experimental.cardinality(examples)
        else:
            len_examples = len(examples)
        if ex_index % 10000 == 0:
            logger.info("Writing example %d/%d" % (ex_index, len_examples))

        # convert to id

        inputs = tokenizer.encode_plus(example.text_a, example.text_b,example.label_roots,root_label_map, add_special_tokens=True, max_length=max_length,)

        input_ids, token_type_ids = inputs["input_ids"], inputs["token_type_ids"]
        root_ids=inputs["root_ids"] #
 


        attention_mask = [1 if mask_padding_with_zero else 0] * len(input_ids)

        # Zero-pad up to the sequence length.
        padding_length = max_length - len(input_ids)
        if pad_on_left:
            input_ids = ([pad_token] * padding_length) + input_ids
            attention_mask = ([0 if mask_padding_with_zero else 1] * padding_length) + attention_mask
            token_type_ids = ([pad_token_segment_id] * padding_length) + token_type_ids
        else:
            input_ids = input_ids + ([pad_token] * padding_length)
            attention_mask = attention_mask + ([0 if mask_padding_with_zero else 1] * padding_length)
            token_type_ids = token_type_ids + ([pad_token_segment_id] * padding_length)
            root_ids +=[0] * padding_length


        assert len(input_ids) == max_length, "Error with input length {} vs {}".format(len(input_ids), max_length)
        assert len(attention_mask) == max_length, "Error with input length {} vs {}".format(
            len(attention_mask), max_length
        )
        assert len(token_type_ids) == max_length, "Error with input length {} vs {}".format(
            len(token_type_ids), max_length
        )
        assert len(root_ids) == max_length, "Error with input length {} vs {}".format(
            len(root_ids), max_length
        )
        if output_mode == "classification":
            label = label_map[example.label]
        elif output_mode == "regression":
            label = float(example.label)
        else:
            raise KeyError(output_mode)


        features.append(
            InputFeatures(root_ids=root_ids,
                input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids, label=label
            )
        )


    if is_tf_available() and is_tf_dataset:

        def gen():
            for ex in features:
                yield (
                    {
                        "input_ids": ex.input_ids,
                        "attention_mask": ex.attention_mask,
                        "token_type_ids": ex.token_type_ids,
                    },
                    ex.label,
                )

        return tf.data.Dataset.from_generator(
            gen,
            ({"input_ids": tf.int32, "attention_mask": tf.int32, "token_type_ids": tf.int32}, tf.int64),
            (
                {
                    "input_ids": tf.TensorShape([None]),
                    "attention_mask": tf.TensorShape([None]),
                    "token_type_ids": tf.TensorShape([None]),
                },
                tf.TensorShape([]),
            ),
        )

    return features




class LapProcessor(DataProcessor):
    """Processor for ATSA task. The aspect terms must one word/ phrase in the sentence."""

    def get_train_examples(self, data_dir):
        return self._create_examples(
            self._read_tsv(os.path.join(data_dir, "lap.train.token.tsv")), "train")

    def get_dev_examples(self, data_dir):
        return self._create_examples(
            self._read_tsv(os.path.join(data_dir, "lap.dev.token.tsv")), "dev")

    def get_test_examples(self, data_dir):
        return self._create_examples(
            self._read_tsv(os.path.join(data_dir, "lap.test.token.tsv")), "test")

    def get_labels(self):

        return ["-1", "0", "1"]  # negative, neutral, positive

    def get_root_label(self):

        return ["Non-Aspect", "Aspect"]
#@

    def _create_examples(self, lines, set_type):

        examples = []
        for (i, line) in enumerate(lines):
            guid = "%s-%s" % (set_type, i)  # train-1,...
            text_a = line[0]  # sentence
            text_b = line[1]  # aspect_term
            label = line[2]
            label_roots= line[3] # root label

            examples.append(
                InputExample(guid=guid, text_a=text_a, text_b=text_b, label=label,label_roots=label_roots))#
        return examples

class ResProcessor(DataProcessor):

    def get_train_examples(self, data_dir):
        return self._create_examples(
            self._read_tsv(os.path.join(data_dir, "rest14.train.token.tsv")), "train")

    def get_dev_examples(self, data_dir):
        return self._create_examples(
            self._read_tsv(os.path.join(data_dir, "rest14.dev.token.tsv")), "dev")

    def get_test_examples(self, data_dir):

        return self._create_examples(
            self._read_tsv(os.path.join(data_dir, "rest14.test.token.tsv")), "test")

    def get_labels(self):

        return ["-1", "0", "1"]
#@
    def get_root_label(self):
        return ["Non-Aspect", "Aspect"]


    def _create_examples(self, lines, set_type):

        examples = []
        for (i, line) in enumerate(lines):
            guid = "%s-%s" % (set_type, i)
            text_a = line[0]
            text_b = line[1]
            label = line[2]
            label_roots= line[3]

            examples.append(
                InputExample(guid=guid, text_a=text_a, text_b=text_b, label=label,label_roots=label_roots))
        return examples

class ResProcessor15(DataProcessor):

    def get_train_examples(self, data_dir):

        return self._create_examples(
            self._read_tsv(os.path.join(data_dir, "rest15.train.token.tsv")), "train")

    def get_dev_examples(self, data_dir):

        return self._create_examples(
            self._read_tsv(os.path.join(data_dir, "rest15.dev.token.tsv")), "dev")

    def get_test_examples(self, data_dir):

        return self._create_examples(
            self._read_tsv(os.path.join(data_dir, "rest15.test.token.tsv")), "test")

    def get_labels(self):

        return ["-1", "0", "1"]
#@
    def get_root_label(self):

        return ["Non-Aspect", "Aspect"]

    def _create_examples(self, lines, set_type):

        examples = []
        for (i, line) in enumerate(lines):
            guid = "%s-%s" % (set_type, i)
            text_a = line[0]
            text_b = line[1]
            label = line[2]
            label_roots= line[3]

            examples.append(
                InputExample(guid=guid, text_a=text_a, text_b=text_b, label=label,label_roots=label_roots))
        return examples

class ResProcessor16(DataProcessor):


    def get_train_examples(self, data_dir):

        return self._create_examples(
            self._read_tsv(os.path.join(data_dir, "rest16.train.token.tsv")), "train")

    def get_dev_examples(self, data_dir):

        return self._create_examples(
            self._read_tsv(os.path.join(data_dir, "rest16.dev.token.tsv")), "dev")

    def get_test_examples(self, data_dir):

        return self._create_examples(
            self._read_tsv(os.path.join(data_dir, "rest16.test.token.tsv")), "test")

    def get_labels(self):

        return ["-1", "0", "1"]
#@
    def get_root_label(self):

        return ["Non-Aspect", "Aspect"]

    def _create_examples(self, lines, set_type):

        examples = []
        for (i, line) in enumerate(lines):
            guid = "%s-%s" % (set_type, i)
            text_a = line[0]
            text_b = line[1]
            label = line[2]
            label_roots= line[3]

            examples.append(
                InputExample(guid=guid, text_a=text_a, text_b=text_b, label=label,label_roots=label_roots))
        return examples

class TwiProcessor(DataProcessor):


    def get_train_examples(self, data_dir):
        return self._create_examples(
            self._read_tsv(os.path.join(data_dir, "twi.train.token.tsv")), "train")

    def get_dev_examples(self, data_dir):
        return self._create_examples(
            self._read_tsv(os.path.join(data_dir, "twi.dev.token.tsv")), "dev")

    def get_test_examples(self, data_dir):
        return self._create_examples(
            self._read_tsv(os.path.join(data_dir, "twi.test.token.tsv")), "test")

    def get_labels(self):

        return ["-1", "0", "1"]

    def get_root_label(self):

        return ["Non-Aspect", "Aspect"]

    def _create_examples(self, lines, set_type):

        examples = []
        for (i, line) in enumerate(lines):
            guid = "%s-%s" % (set_type, i)
            text_a = line[0]
            text_b = line[1]
            label = line[2]
            label_roots= line[3]

            examples.append(
                InputExample(guid=guid, text_a=text_a, text_b=text_b, label=label,label_roots=label_roots))
        return examples





absa_tasks_num_labels = {
    "lap": 3,
    "rest": 3,
    "rest15": 3,
    "rest16": 3,
    "twi": 3,

}

absa_processors = {
    "lap": LapProcessor,
    "rest": ResProcessor,
    "rest15": ResProcessor15,
    "rest16": ResProcessor16,
    "twi": TwiProcessor,
}

absa_output_modes = {
    "lap": "classification",
    "rest": "classification",
    "rest15": "classification",
    "rest16": "classification",
    "twi": "classification",
}

from sklearn.metrics import matthews_corrcoef, f1_score
def simple_accuracy(preds, labels):
    return (preds == labels).mean()

def acc_and_f1(preds, labels):
    acc = simple_accuracy(preds, labels)
    f1 = f1_score(y_true=labels, y_pred=preds, average='macro')
    return {
        "acc": acc,
        "f1": f1,
    }